package net.pimpas.web.jsf.util;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;

import javax.faces.application.StateManager;
import javax.faces.component.UIComponent;
import javax.faces.component.html.HtmlCommandLink;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.pimpas.logging.serviceprovider.LoggingServiceProvider;
import net.pimpas.logging.serviceprovider.LoggingServiceProviderFactoryImpl;
import net.pimpas.util.reflection.ReflectionUtil;
import net.pimpas.web.jsf.components.ajax.UIAjax;
import net.pimpas.web.jsf.components.ajax.context.ResponseWriterAjaxWrapper;

import org.apache.myfaces.renderkit.html.HtmlResponseStateManager;
import org.apache.myfaces.shared_impl.util.StateUtils;
import org.dom4j.Element;

/**
 * 
 * @author pc
 * Tkz tobago
 *
 */
public abstract class AjaxUtils {
	
	public static final String DEFAULT_COMPONENT_FAMILY		= "pimpas.jsf.components.ajax.default";
	public static final String DEFAULT_RENDERER_TYPE		= "pimpas.jsf.renders.ajax.default";
	
	private static LoggingServiceProvider logger = 
		LoggingServiceProviderFactoryImpl.getLoggingServiceProvider(AjaxUtils.class);
	
	public static void ensureCorrectClient(ExternalContext context) {
		ServletResponse response = (ServletResponse) context.getResponse();
		ServletRequest request = (ServletRequest) context.getRequest();
		if(response instanceof HttpServletResponse) {
			HttpServletResponse httpResponse = (HttpServletResponse) response;
			httpResponse.setContentType("text/xml");
			httpResponse.setHeader("Cache-Control", "no-cache");
		}
		if(request instanceof HttpServletRequest) {
			HttpServletRequest httpRequest = (HttpServletRequest) request;
			try {
				httpRequest.setCharacterEncoding("UTF-8");
			} catch (UnsupportedEncodingException e) {
			}
		}
	}
	
	public static void saveView(FacesContext context) {
		
		synchronized(context) {
			// save state
	        final StateManager stateManager = context.getApplication().getStateManager();
	        StateManager.SerializedView serializedView = stateManager.saveSerializedView(context);
	        try {
				stateManager.writeState(context, serializedView);
			} catch (IOException e) {
				logger.fatal("[AJAXPHASERENDERER]: Fatal error! Can't save the view state... see the full log!", e);
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public static void attachAjaxEvent(UIAjax component, String pimpasFunction, String keyCheck) {
		UIAjax populate = (UIAjax) component;
		UIComponent parent = populate.getParent();
		// check event
		PropertyDescriptor pd = ReflectionUtil.getPropertyDescriptor(parent.getClass(), populate.getEvent());
		
		if(pd == null) {
			String msg = "[AJAXUtils]: Cannot find event ["+populate.getEvent()+"] in parent class ["+parent.getClass().getSimpleName()+"]";
			logger.fatal(msg);
			throw new RuntimeException(msg);
		}
		try {
			if(parent.getAttributes().get(keyCheck)==null) {
				String func = pimpasFunction;
				String previous = (String) pd.getReadMethod().invoke(parent);
				if(previous != null && !ReflectionUtil.isSubclass(parent.getClass(), HtmlCommandLink.class))
					func = previous + ((previous.charAt(previous.length()-1) != ';') ? ";" : "") + func;
				pd.getWriteMethod().invoke(parent, func);
				parent.getAttributes().put(keyCheck, new Boolean(true));
			}
		} catch (Throwable e) {
			logger.fatal(e);
			throw new RuntimeException(e);
		}
	}

	@Deprecated
	public static void renderJsfSequence(ResponseWriterAjaxWrapper wrapper, Element root) {
        Map map = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
        //TODO find jsf_sequence parameter
        Integer sequence = (Integer) map.get("jsf_sequence");
        
        root.addElement("jsf_sequence").setText(String.valueOf(sequence));
	}

	public static void renderViewState(ResponseWriterAjaxWrapper wrapper, Element root) {
		FacesContext context = FacesContext.getCurrentInstance();
		StateManager manager = context.getApplication().getStateManager();
		
	    final int TREE_PARAM = 0;
	    final int STATE_PARAM = 1;
	    final int VIEWID_PARAM = 2;
		
        Object[] savedState = new Object[3];
        StateManager.SerializedView serializedview = manager.saveSerializedView(context);
        
		if(manager.isSavingStateInClient(context)) {
			//TODO copy from HtmlResponseStateManager in myfaces 1.1.5
			//u need change this in next version, REMEMBER!!

            Object treeStruct = serializedview.getStructure();
            Object compStates = serializedview.getState();

            if(treeStruct != null)
                savedState[TREE_PARAM]=treeStruct;

            if (compStates != null)
                savedState[STATE_PARAM] = compStates;
            
	        savedState[VIEWID_PARAM] = context.getViewRoot().getViewId();

		} else {
            // write viewSequence
            Object treeStruct = serializedview.getStructure();
            if (treeStruct != null)
                if (treeStruct instanceof String)
                    savedState[TREE_PARAM] = treeStruct;
    	}
		
		// TODO dont use direct dom4j! use interface provided by pimpas-xml
		Element el = root.addElement("viewState");
		el.addAttribute("id", HtmlResponseStateManager.STANDARD_STATE_SAVING_PARAM);
		el.addCDATA(StateUtils.construct(savedState,
                context.getExternalContext()));
	}
}